package chc

import (
	"database/sql"
	"fmt"
	"reflect"
	"regexp"
	"strings"
	"time"
)

func dbInsert(mi *modelInfo, ind reflect.Value, tz *time.Location) (err error) {

	var (
		names  = make([]string, 0, len(mi.fields.dbcols))
		values []interface{}
	)
	if values, err = collectValues(mi, ind, mi.fields.dbcols, false, true, &names, tz); nil != err {
		return err
	}

	if err = dbInsertValue(mi, names, values); nil != err {
		return err
	}

	return nil
}

func dbInsertValue(mi *modelInfo, names []string, values []interface{}) error {

	var fieldsSqlArr []string
	for _, name := range names {
		fieldsSqlArr = append(fieldsSqlArr, fmt.Sprintf("%s%s%s", FieldQuote, name, FieldQuote))
	}

	valSqlStr := genValParams(len(names))
	valSqlStr = genValsParams(len(values)/len(names), valSqlStr)

	table := fmt.Sprintf("%s%s%s", TableQuote, mi.table, TableQuote)

	sqlStr := fmt.Sprintf("INSERT INTO %s (%s) VALUES %s", table, strings.Join(fieldsSqlArr, ", "), valSqlStr)

	if _, err := chSession.Connect.Exec(sqlStr, values...); nil != err {
		return err
	}

	return nil
}

func dbDelete(mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) {

	var whereCols []string
	var args []interface{}

	// if specify cols length > 0, then use it for where condition.
	if len(cols) > 0 {
		var err error
		whereCols = make([]string, 0, len(cols))
		args, err = collectValues(mi, ind, cols, false, false, &whereCols, tz)
		if err != nil {
			return 0, err
		}
	} else {
		// default use pk value as where condtion.
		pkColumn, pkValue, ok := getExistPk(mi, ind)
		if !ok {
			return 0, ErrMissPK
		}
		whereCols = []string{pkColumn}
		args = append(args, pkValue)
	}

	sep := fmt.Sprintf("%s = ? AND %s", FieldQuote, FieldQuote)
	wheres := strings.Join(whereCols, sep)

	query := fmt.Sprintf("ALTER TABLE %s%s%s DELETE WHERE %s%s%s = ?", TableQuote, mi.table, TableQuote, FieldQuote, wheres, FieldQuote)

	for _, singleConn := range chSession.SingleConns {
		if _, err := singleConn.Exec(query); nil != err {
			_ = fmt.Errorf(err.Error())
		}
	}

	return 0, nil
}

func dbUpdate(mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) (int64, error) {
	pkName, pkValue, ok := getExistPk(mi, ind)
	if !ok {
		return 0, ErrMissPK
	}

	var setNames []string

	// if specify cols length is zero, then commit all columns.
	if len(cols) == 0 {
		cols = mi.fields.dbcols
		setNames = make([]string, 0, len(mi.fields.dbcols)-1)
	} else {
		setNames = make([]string, 0, len(cols))
	}

	setValues, err := collectValues(mi, ind, cols, true, false, &setNames, tz)
	if err != nil {
		return 0, err
	}

	var findAutoNowAdd, findAutoNow bool
	var index int
	for i, col := range setNames {
		if mi.fields.GetByColumn(col).autoNowAdd {
			index = i
			findAutoNowAdd = true
		}
		if mi.fields.GetByColumn(col).autoNow {
			findAutoNow = true
		}
	}
	if findAutoNowAdd {
		setNames = append(setNames[0:index], setNames[index+1:]...)
		setValues = append(setValues[0:index], setValues[index+1:]...)
	}

	if !findAutoNow {
		for col, info := range mi.fields.columns {
			if info.autoNow {
				setNames = append(setNames, col)
				setValues = append(setValues, time.Now())
			}
		}
	}

	setValues = append(setValues, pkValue)

	sep := fmt.Sprintf("%s = ?, %s", FieldQuote, FieldQuote)
	setColumns := strings.Join(setNames, sep)

	query := fmt.Sprintf("ALTER TABLE %s%s%s UPDATE %s%s%s = ? WHERE %s%s%s = ?", TableQuote, mi.table, TableQuote, FieldQuote, setColumns, FieldQuote, FieldQuote, pkName, FieldQuote)

	for _, singleConn := range chSession.SingleConns {
		if _, err := singleConn.Exec(query); nil != err {
			_ = fmt.Errorf(err.Error())
		}
	}

	return 0, err
}

func dbRead(mi *modelInfo, ind reflect.Value, tz *time.Location, cols []string) error {

	var (
		whereCols []string
		args      []interface{}
	)

	// if specify cols length > 0, then use it for where condition.
	if len(cols) > 0 {
		var err error
		whereCols = make([]string, 0, len(cols))
		args, err = collectValues(mi, ind, cols, false, false, &whereCols, tz)
		if err != nil {
			return err
		}
	} else {
		// default use pk value as where condtion.
		pkColumn, pkValue, ok := getExistPk(mi, ind)
		if !ok {
			return ErrMissPK
		}
		whereCols = []string{pkColumn}
		args = append(args, pkValue)
	}

	for index, _ := range whereCols {
		whereCols[index] = fmt.Sprintf("%s%s%s = ?", FieldQuote, whereCols[index], FieldQuote)
	}

	var colArr []string
	for index, _ := range mi.fields.dbcols {
		colArr = append(colArr, fmt.Sprintf("%s%s%s", FieldQuote, mi.fields.dbcols[index], FieldQuote))
	}

	table := fmt.Sprintf("%s%s%s", TableQuote, fixTableName(mi.table), TableQuote)
	query := fmt.Sprintf("SELECT %s FROM %s WHERE %s", strings.Join(colArr, ", "), table, strings.Join(whereCols, " AND "))

	refs := make([]interface{}, len(mi.fields.dbcols))
	for i := range refs {
		var ref interface{}
		refs[i] = &ref
	}

	row := chSession.Connect.QueryRow(query, args...)
	if err := row.Scan(refs...); err != nil {
		if err == sql.ErrNoRows {
			return ErrNoRows
		}
		return err
	}

	elm := reflect.New(mi.addrField.Elem().Type())
	mind := reflect.Indirect(elm)
	setColsValues(mi, &mind, mi.fields.dbcols, refs, tz)
	ind.Set(mind)
	return nil
}

func dbReadBatch(qs *QuerySet, mi *modelInfo, cond *Condition, container interface{}, tz *time.Location, cols []string) (int64, error) {

	val := reflect.ValueOf(container)
	ind := reflect.Indirect(val)

	errTyp := true
	one := true
	isPtr := true

	if val.Kind() == reflect.Ptr {
		fn := ""
		if ind.Kind() == reflect.Slice {
			one = false
			typ := ind.Type().Elem()
			switch typ.Kind() {
			case reflect.Ptr:
				fn = getFullName(typ.Elem())
			case reflect.Struct:
				isPtr = false
				fn = getFullName(typ)
			}
		} else {
			fn = getFullName(ind.Type())
		}
		errTyp = fn != mi.fullName
	}

	if errTyp {
		if one {
			panic(fmt.Errorf("wrong object type `%s` for rows scan, need *%s", val.Type(), mi.fullName))
		} else {
			panic(fmt.Errorf("wrong object type `%s` for rows scan, need *[]*%s or *[]%s", val.Type(), mi.fullName, mi.fullName))
		}
	}

	rlimit := qs.limit
	offset := qs.offset

	var tCols []string
	if len(cols) > 0 {
		tCols = make([]string, 0, len(cols))
		for _, col := range cols {
			if fi, ok := mi.fields.GetByAny(col); ok {
				tCols = append(tCols, fi.column)
			} else {
				return 0, fmt.Errorf("wrong field/column name `%s`", col)
			}
		}

	} else {
		tCols = mi.fields.dbcols
	}

	colsNum := len(tCols)

	sels := mi.fields.GetQueryColumnSQL()
	where, args := qs.GetCondSQL(cond, tz)
	groupBy := qs.GetGroupBySQL(qs.groups)
	orderBy := qs.GetOrderBySQL(qs.orders)
	limit := qs.GetLimitSQL(offset, rlimit)

	sqlSelect := "SELECT"
	if qs.distinct {
		sqlSelect += " DISTINCT"
	}
	query := fmt.Sprintf("%s %s FROM %s %s%s%s%s", sqlSelect, sels, fixTableName(mi.table), where, groupBy, orderBy, limit)

	var rs *sql.Rows
	var err error
	if qs != nil && qs.forContext {
		rs, err = chSession.Connect.QueryContext(qs.ctx, query, args...)
		if err != nil {
			return 0, err
		}
	} else {
		rs, err = chSession.Connect.Query(query, args...)
		if err != nil {
			return 0, err
		}
	}

	refs := make([]interface{}, colsNum)
	for i := range refs {
		var ref interface{}
		refs[i] = &ref
	}

	defer rs.Close()

	slice := ind

	var cnt int64
	for rs.Next() {
		if one && cnt == 0 || !one {
			if err := rs.Scan(refs...); err != nil {
				return 0, err
			}

			elm := reflect.New(mi.addrField.Elem().Type())
			mind := reflect.Indirect(elm)

			setColsValues(mi, &mind, tCols, refs[:len(tCols)], tz)

			if one {
				ind.Set(mind)
			} else {
				if cnt == 0 {
					// you can use a empty & caped container list
					// orm will not replace it
					if ind.Len() != 0 {
						// if container is not empty
						// create a new one
						slice = reflect.New(ind.Type()).Elem()
					}
				}

				if isPtr {
					slice = reflect.Append(slice, mind.Addr())
				} else {
					slice = reflect.Append(slice, mind)
				}
			}
		}
		cnt++
	}

	if !one {
		if cnt > 0 {
			ind.Set(slice)
		} else {
			// when a result is empty and container is nil
			// to set a empty container
			if ind.IsNil() {
				ind.Set(reflect.MakeSlice(ind.Type(), 0, 0))
			}
		}
	}

	return cnt, nil
}

func dbCount(qs *QuerySet, mi *modelInfo, tz *time.Location) (cnt int64, err error) {

	var (
		where, args = qs.GetCondSQL(qs.cond, tz)
		groupBy     = qs.GetGroupBySQL(qs.groups)
		table       = fmt.Sprintf("%s%s%s", TableQuote, fixTableName(mi.table), TableQuote)
		query       = fmt.Sprintf("SELECT COUNT(*) FROM %s %s %s", table, where, groupBy)
		row         *sql.Row
	)

	if qs != nil && qs.forContext {
		row = chSession.Connect.QueryRowContext(qs.ctx, query, args...)
	} else {
		row = chSession.Connect.QueryRow(query, args...)
	}
	err = row.Scan(&cnt)
	return
}

func collectValues(mi *modelInfo, ind reflect.Value, cols []string, skipAuto bool, insert bool, names *[]string, tz *time.Location) (values []interface{}, err error) {

	if names == nil {
		ns := make([]string, 0, len(cols))
		names = &ns
	}
	values = make([]interface{}, 0, len(cols))

	for _, column := range cols {
		var fi *fieldInfo
		if fi, _ = mi.fields.GetByAny(column); fi != nil {
			column = fi.column
		} else {
			panic(fmt.Errorf("wrong db field/column name `%s` for model `%s`", column, mi.fullName))
		}
		if !fi.dbcol || fi.auto && skipAuto {
			continue
		}
		value, err := collectFieldValue(mi, fi, ind, insert, tz)
		if err != nil {
			return nil, err
		}

		*names, values = append(*names, column), append(values, value)
	}

	return
}

func collectFieldValue(mi *modelInfo, fi *fieldInfo, ind reflect.Value, insert bool, tz *time.Location) (interface{}, error) {

	var value interface{}
	if fi.pk {
		_, value, _ = getExistPk(mi, ind)
		return value, nil
	}

	field := ind.FieldByIndex(fi.fieldIndex)
	if fi.isFielder {
		f := field.Addr().Interface().(Fielder)
		value = f.RawValue()
	} else {
		switch fi.fieldType {
		case TypeBooleanField:
			if nb, ok := field.Interface().(sql.NullBool); ok {
				value = nil
				if nb.Valid {
					value = nb.Bool
				}
			} else if field.Kind() == reflect.Ptr {
				if field.IsNil() {
					value = nil
				} else {
					value = field.Elem().Bool()
				}
			} else {
				value = field.Bool()
			}
		case TypeVarCharField, TypeCharField, TypeTextField, TypeJSONField, TypeJsonbField:
			if ns, ok := field.Interface().(sql.NullString); ok {
				value = nil
				if ns.Valid {
					value = ns.String
				}
			} else if field.Kind() == reflect.Ptr {
				if field.IsNil() {
					value = nil
				} else {
					value = field.Elem().String()
				}
			} else {
				value = field.String()
			}
		case TypeFloatField, TypeDecimalField:
			if nf, ok := field.Interface().(sql.NullFloat64); ok {
				value = nil
				if nf.Valid {
					value = nf.Float64
				}
			} else if field.Kind() == reflect.Ptr {
				if field.IsNil() {
					value = nil
				} else {
					value = field.Elem().Float()
				}
			} else {
				vu := field.Interface()
				if _, ok := vu.(float32); ok {
					value, _ = StrTo(ToStr(vu)).Float64()
				} else {
					value = field.Float()
				}
			}
		case TypeTimeField, TypeDateField, TypeDateTimeField:
			value = field.Interface()
			if t, ok := value.(time.Time); ok {
				TimeToDB(&t, tz)
				if t.IsZero() {
					value = nil
				} else {
					value = t
				}
			}
		default:
			switch {
			case fi.fieldType&IsPositiveIntegerField > 0:
				if field.Kind() == reflect.Ptr {
					if field.IsNil() {
						value = nil
					} else {
						value = field.Elem().Uint()
					}
				} else {
					value = field.Uint()
				}
			case fi.fieldType&IsIntegerField > 0:
				if ni, ok := field.Interface().(sql.NullInt64); ok {
					value = nil
					if ni.Valid {
						value = ni.Int64
					}
				} else if field.Kind() == reflect.Ptr {
					if field.IsNil() {
						value = nil
					} else {
						value = field.Elem().Int()
					}
				} else {
					value = field.Int()
				}
			case fi.fieldType&IsRelField > 0:
				if field.IsNil() {
					value = nil
				} else {
					if _, vu, ok := getExistPk(fi.relModelInfo, reflect.Indirect(field)); ok {
						value = vu
					} else {
						value = nil
					}
				}
				if !fi.null && value == nil {
					return nil, fmt.Errorf("field `%s` cannot be NULL", fi.fullName)
				}
			}
		}
	}
	switch fi.fieldType {
	case TypeTimeField, TypeDateField, TypeDateTimeField:
		if fi.autoNow || fi.autoNowAdd && insert {
			if insert {
				if t, ok := value.(time.Time); ok && !t.IsZero() {
					break
				}
			}
			tnow := time.Now()
			TimeToDB(&tnow, tz)
			value = tnow
			if fi.isFielder {
				f := field.Addr().Interface().(Fielder)
				f.SetRaw(tnow.In(DefaultTimeLoc))
			} else if field.Kind() == reflect.Ptr {
				v := tnow.In(DefaultTimeLoc)
				field.Set(reflect.ValueOf(&v))
			} else {
				field.Set(reflect.ValueOf(tnow.In(DefaultTimeLoc)))
			}
		}
	case TypeJSONField, TypeJsonbField:
		if s, ok := value.(string); (ok && len(s) == 0) || value == nil {
			if fi.colDefault && fi.initial.Exist() {
				value = fi.initial.String()
			} else {
				value = nil
			}
		}
	}

	return value, nil
}

// generate sql with replacing operator string placeholders and replaced values.
func GenerateOperatorSQL(mi *modelInfo, fi *fieldInfo, operator string, args []interface{}, tz *time.Location) (string, []interface{}) {
	var sql string
	params := getFlatParams(fi, args, tz)

	if len(params) == 0 {
		panic(fmt.Errorf("operator `%s` need at least one args", operator))
	}
	arg := params[0]

	switch operator {
	case "in":
		marks := make([]string, len(params))
		for i := range marks {
			marks[i] = "?"
		}
		sql = fmt.Sprintf("IN (%s)", strings.Join(marks, ", "))
	case "between":
		if len(params) != 2 {
			panic(fmt.Errorf("operator `%s` need 2 args not %d", operator, len(params)))
		}
		sql = "BETWEEN ? AND ?"
	default:
		if len(params) > 1 {
			panic(fmt.Errorf("operator `%s` need 1 args not %d", operator, len(params)))
		}
		sql = OperatorSQL(operator)
		switch operator {
		case "exact":
			if arg == nil {
				params[0] = "IS NULL"
			}
		case "iexact", "contains", "icontains", "startswith", "endswith", "istartswith", "iendswith":
			param := strings.Replace(ToStr(arg), `%`, `\%`, -1)
			switch operator {
			case "iexact":
			case "contains", "icontains":
				param = fmt.Sprintf("%%%s%%", param)
			case "startswith", "istartswith":
				param = fmt.Sprintf("%s%%", param)
			case "endswith", "iendswith":
				param = fmt.Sprintf("%%%s", param)
			}
			params[0] = param
		case "isnull":
			if b, ok := arg.(bool); ok {
				if b {
					sql = "IS NULL"
				} else {
					sql = "IS NOT NULL"
				}
				params = nil
			} else {
				panic(fmt.Errorf("operator `%s` need a bool value not `%T`", operator, arg))
			}
		}
	}
	return sql, params
}

// gernerate sql string with inner function, such as UPPER(text).
func GenerateOperatorLeftCol(*fieldInfo, string, *string) {
	// default not use
}

func setColsValues(mi *modelInfo, ind *reflect.Value, cols []string, values []interface{}, tz *time.Location) {
	for i, column := range cols {
		val := reflect.Indirect(reflect.ValueOf(values[i])).Interface()

		fi := mi.fields.GetByColumn(column)

		field := ind.FieldByIndex(fi.fieldIndex)

		value, err := convertValueFromDB(fi, val, tz)
		if err != nil {
			panic(fmt.Errorf("Raw value: `%v` %s\n", val, err.Error()))
		}

		_, err = setFieldValue(fi, value, field)

		if err != nil {
			panic(fmt.Errorf("Raw value: `%v` %s\n", val, err.Error()))
		}
	}
}

func convertValueFromDB(fi *fieldInfo, val interface{}, tz *time.Location) (interface{}, error) {
	if val == nil {
		return nil, nil
	}

	var value interface{}
	var tErr error

	var str *StrTo
	switch v := val.(type) {
	case []byte:
		s := StrTo(string(v))
		str = &s
	case string:
		s := StrTo(v)
		str = &s
	}

	fieldType := fi.fieldType

setValue:
	switch {
	case fieldType == TypeBooleanField:
		if str == nil {
			switch v := val.(type) {
			case int64:
				b := v == 1
				value = b
			default:
				s := StrTo(ToStr(v))
				str = &s
			}
		}
		if str != nil {
			b, err := str.Bool()
			if err != nil {
				tErr = err
				goto end
			}
			value = b
		}
	case fieldType == TypeVarCharField || fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField:
		if str == nil {
			value = ToStr(val)
		} else {
			value = str.String()
		}
	case fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField:
		if str == nil {
			switch t := val.(type) {
			case time.Time:
				TimeFromDB(&t, tz)
				value = t
			default:
				s := StrTo(ToStr(t))
				str = &s
			}
		}
		if str != nil {
			s := str.String()
			var (
				t   time.Time
				err error
			)
			matched, errr := regexp.MatchString("[0-9]{4}-[0-1][0-9]-[0-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9].[0-9]{3}", s)
			if nil == errr && matched {
				t, err = time.ParseInLocation(formatTimestamp, s, tz)
			} else if len(s) >= 19 {
				s = s[:19]
				t, err = time.ParseInLocation(formatDateTime, s, tz)
			} else if len(s) >= 10 {
				if len(s) > 10 {
					s = s[:10]
				}
				t, err = time.ParseInLocation(formatDate, s, tz)
			} else if len(s) >= 8 {
				if len(s) > 8 {
					s = s[:8]
				}
				t, err = time.ParseInLocation(formatTime, s, tz)
			}
			t = t.In(DefaultTimeLoc)

			if err != nil && s != "00:00:00" && s != "0000-00-00" && s != "0000-00-00 00:00:00" {
				tErr = err
				goto end
			}
			value = t
		}
	case fieldType&IsIntegerField > 0:
		if str == nil {
			s := StrTo(ToStr(val))
			str = &s
		}
		if str != nil {
			var err error
			switch fieldType {
			case TypeBitField:
				_, err = str.Int8()
			case TypeSmallIntegerField:
				_, err = str.Int16()
			case TypeIntegerField:
				_, err = str.Int32()
			case TypeBigIntegerField:
				_, err = str.Int64()
			case TypePositiveBitField:
				_, err = str.Uint8()
			case TypePositiveSmallIntegerField:
				_, err = str.Uint16()
			case TypePositiveIntegerField:
				_, err = str.Uint32()
			case TypePositiveBigIntegerField:
				_, err = str.Uint64()
			}
			if err != nil {
				tErr = err
				goto end
			}
			if fieldType&IsPositiveIntegerField > 0 {
				v, _ := str.Uint64()
				value = v
			} else {
				v, _ := str.Int64()
				value = v
			}
		}
	case fieldType == TypeFloatField || fieldType == TypeDecimalField:
		if str == nil {
			switch v := val.(type) {
			case float64:
				value = v
			default:
				s := StrTo(ToStr(v))
				str = &s
			}
		}
		if str != nil {
			v, err := str.Float64()
			if err != nil {
				tErr = err
				goto end
			}
			value = v
		}
	case fieldType&IsRelField > 0:
		fi = fi.relModelInfo.fields.pk
		fieldType = fi.fieldType
		goto setValue
	}

end:
	if tErr != nil {
		err := fmt.Errorf("convert to `%s` failed, field: %s err: %s", fi.addrValue.Type(), fi.fullName, tErr)
		return nil, err
	}

	return value, nil

}

// set one value to struct column field.
func setFieldValue(fi *fieldInfo, value interface{}, field reflect.Value) (interface{}, error) {

	fieldType := fi.fieldType
	isNative := !fi.isFielder

setValue:
	switch {
	case fieldType == TypeBooleanField:
		if isNative {
			if nb, ok := field.Interface().(sql.NullBool); ok {
				if value == nil {
					nb.Valid = false
				} else {
					nb.Bool = value.(bool)
					nb.Valid = true
				}
				field.Set(reflect.ValueOf(nb))
			} else if field.Kind() == reflect.Ptr {
				if value != nil {
					v := value.(bool)
					field.Set(reflect.ValueOf(&v))
				}
			} else {
				if value == nil {
					value = false
				}
				field.SetBool(value.(bool))
			}
		}
	case fieldType == TypeVarCharField || fieldType == TypeCharField || fieldType == TypeTextField || fieldType == TypeJSONField || fieldType == TypeJsonbField:
		if isNative {
			if ns, ok := field.Interface().(sql.NullString); ok {
				if value == nil {
					ns.Valid = false
				} else {
					ns.String = value.(string)
					ns.Valid = true
				}
				field.Set(reflect.ValueOf(ns))
			} else if field.Kind() == reflect.Ptr {
				if value != nil {
					v := value.(string)
					field.Set(reflect.ValueOf(&v))
				}
			} else {
				if value == nil {
					value = ""
				}
				field.SetString(value.(string))
			}
		}
	case fieldType == TypeTimeField || fieldType == TypeDateField || fieldType == TypeDateTimeField:
		if isNative {
			if value == nil {
				value = time.Time{}
			} else if field.Kind() == reflect.Ptr {
				if value != nil {
					v := value.(time.Time)
					field.Set(reflect.ValueOf(&v))
				}
			} else {
				field.Set(reflect.ValueOf(value))
			}
		}
	case fieldType == TypePositiveBitField && field.Kind() == reflect.Ptr:
		if value != nil {
			v := uint8(value.(uint64))
			field.Set(reflect.ValueOf(&v))
		}
	case fieldType == TypePositiveSmallIntegerField && field.Kind() == reflect.Ptr:
		if value != nil {
			v := uint16(value.(uint64))
			field.Set(reflect.ValueOf(&v))
		}
	case fieldType == TypePositiveIntegerField && field.Kind() == reflect.Ptr:
		if value != nil {
			if field.Type() == reflect.TypeOf(new(uint)) {
				v := uint(value.(uint64))
				field.Set(reflect.ValueOf(&v))
			} else {
				v := uint32(value.(uint64))
				field.Set(reflect.ValueOf(&v))
			}
		}
	case fieldType == TypePositiveBigIntegerField && field.Kind() == reflect.Ptr:
		if value != nil {
			v := value.(uint64)
			field.Set(reflect.ValueOf(&v))
		}
	case fieldType == TypeBitField && field.Kind() == reflect.Ptr:
		if value != nil {
			v := int8(value.(int64))
			field.Set(reflect.ValueOf(&v))
		}
	case fieldType == TypeSmallIntegerField && field.Kind() == reflect.Ptr:
		if value != nil {
			v := int16(value.(int64))
			field.Set(reflect.ValueOf(&v))
		}
	case fieldType == TypeIntegerField && field.Kind() == reflect.Ptr:
		if value != nil {
			if field.Type() == reflect.TypeOf(new(int)) {
				v := int(value.(int64))
				field.Set(reflect.ValueOf(&v))
			} else {
				v := int32(value.(int64))
				field.Set(reflect.ValueOf(&v))
			}
		}
	case fieldType == TypeBigIntegerField && field.Kind() == reflect.Ptr:
		if value != nil {
			v := value.(int64)
			field.Set(reflect.ValueOf(&v))
		}
	case fieldType&IsIntegerField > 0:
		if fieldType&IsPositiveIntegerField > 0 {
			if isNative {
				if value == nil {
					value = uint64(0)
				}
				field.SetUint(value.(uint64))
			}
		} else {
			if isNative {
				if ni, ok := field.Interface().(sql.NullInt64); ok {
					if value == nil {
						ni.Valid = false
					} else {
						ni.Int64 = value.(int64)
						ni.Valid = true
					}
					field.Set(reflect.ValueOf(ni))
				} else {
					if value == nil {
						value = int64(0)
					}
					field.SetInt(value.(int64))
				}
			}
		}
	case fieldType == TypeFloatField || fieldType == TypeDecimalField:
		if isNative {
			if nf, ok := field.Interface().(sql.NullFloat64); ok {
				if value == nil {
					nf.Valid = false
				} else {
					nf.Float64 = value.(float64)
					nf.Valid = true
				}
				field.Set(reflect.ValueOf(nf))
			} else if field.Kind() == reflect.Ptr {
				if value != nil {
					if field.Type() == reflect.TypeOf(new(float32)) {
						v := float32(value.(float64))
						field.Set(reflect.ValueOf(&v))
					} else {
						v := value.(float64)
						field.Set(reflect.ValueOf(&v))
					}
				}
			} else {

				if value == nil {
					value = float64(0)
				}
				field.SetFloat(value.(float64))
			}
		}
	case fieldType&IsRelField > 0:
		if value != nil {
			fieldType = fi.relModelInfo.fields.pk.fieldType
			mf := reflect.New(fi.relModelInfo.addrField.Elem().Type())
			field.Set(mf)
			f := mf.Elem().FieldByIndex(fi.relModelInfo.fields.pk.fieldIndex)
			field = f
			goto setValue
		}
	}

	if !isNative {
		fd := field.Addr().Interface().(Fielder)
		err := fd.SetRaw(value)
		if err != nil {
			err = fmt.Errorf("converted value `%v` set to Fielder `%s` failed, err: %s", value, fi.fullName, err)
			return nil, err
		}
	}

	return value, nil
}

func OperatorSQL(operator string) string {
	return chOperators[operator]
}

func fixTableName(table string) string {
	if chSession.Config.IsCluster {
		return fmt.Sprintf("%s%s%s", TableQuote, table+"_all", TableQuote)
	} else {
		return fmt.Sprintf("%s%s%s", TableQuote, table, TableQuote)
	}
}
